package auth

import (
	"fmt"
	"go.getaway.adminspro.padmins.com/untils"
	"go.getaway.adminspro.padmins.com/untils/db"
	"go.getaway.adminspro.padmins.com/untils/di"
	"strings"
)

type Auth struct {
	Di             *di.Di
	Db             *db.Db
	data           map[string]interface{}
	allowResources []interface{}
}

func (a *Auth) SetData(data map[string]interface{}) {
	a.data = data
}

func (a *Auth) GetData() map[string]interface{} {
	return a.data
}

func (a *Auth) Get(key string) interface{} {
	if val, ok := a.data[key]; ok {
		return val
	} else {
		return ""
	}
}

func (a *Auth) Init(token string) bool {
	tokenData := strings.Split(token, "-")
	if len(tokenData) < 2 {
		return false
	}

	Id := tokenData[0]
	Token := tokenData[1]

	Db := a.Db
	row, _ := Db.Find("select * from admin where id=?", Id)
	if row == nil {
		return false
	}

	if row["token"] != Token {
		return false
	}

	delete(row, "password")
	delete(row, "salt")
	delete(row, "loginfailure")
	row["roles"] = untils.ArrayColumnIndex(a.GetRolesByAdminId(row["id"]), "name", "id")
	a.SetData(row)
	return true
}

func (a *Auth) SetAllowResources(allowResources []interface{}) {
	a.allowResources = allowResources
}

func (a *Auth) Permission(resource string) bool {
	if untils.InArray(resource, a.allowResources) {
		return true
	}

	resourceIds := a.GetResourceIdsByAdminId(a.data["id"])

	if untils.InArray(int64(0), resourceIds) {
		return true
	}

	Db := a.Db
	row, e := Db.Find("select * from admin_resource where resource=? or path=?", resource, strings.ReplaceAll(resource, ".", "/"))
	if e != nil {
		panic(e)
	}
	if row == nil {
		return true
	}
	if untils.InArray(row["id"], resourceIds) {
		return true
	}

	return false
}

func (a *Auth) IsLogin() bool {
	if a.data != nil {
		return true
	}
	return false
}

func (a Auth) GetRolesByAdminId(adminId interface{}) []map[string]interface{} {
	Db := a.Db
	rows, e := Db.Query("select ag.* from admin_role_access aga join admin_role ag on aga.role_id=ag.id where aga.admin_id=?", adminId)
	if e != nil {
		panic(e)
	}
	return rows
}

func (a Auth) GetResourceIdsByAdminId(adminId interface{}) []interface{} {
	Db := a.Db
	rows, e := Db.Query("select agra.resource_id from admin_role_access aga "+
		"join admin_role_resource_access agra on aga.role_id=agra.role_id where aga.admin_id=?", adminId)

	if e != nil {
		panic(e)
	}

	return untils.ArrayColumn(rows, "resource_id")
}

func (a Auth) GetResource(adminId ...interface{}) []map[string]interface{} {
	where := ""
	bindValue := []interface{}{}
	if len(adminId) > 0 {
		resourceIds := a.GetResourceIdsByAdminId(adminId[0])
		if !untils.InArray(int64(0), resourceIds) {
			where = "and id in (?)"
			bindValue = resourceIds
		}
	}

	Db := a.Db
	rows, e := Db.Query(fmt.Sprintf("select * from admin_resource where status=1 %s", where), bindValue)
	for _, row := range rows {
		if row["resource"] == "" {
			row["resource"] = strings.ReplaceAll(row["path"].(string), "/", ".")
		}
		row["path"] = "/" + strings.Trim(row["path"].(string), "/")
	}
	if e != nil {
		panic(e)
	}
	return rows
}

func (a Auth) GetChildrenRoleIds(adminId interface{}, withself ...bool) []interface{} {
	with := false
	if len(withself) > 0 {
		with = withself[0]
	}
	adminRoleIds := a.GetRoleIdsByAdminId(adminId)
	roles := a.getRoles()
	tree := untils.NewTree().Init(roles)
	data := []interface{}{}
	for _, roleId := range adminRoleIds {
		data = append(data, tree.GetChildrenIds(roleId, with)...)
	}
	return data
}

func (a Auth) GetRoleIdsByAdminId(adminId interface{}) []interface{} {
	Db := a.Db
	rows, e := Db.Query("select role_id from admin_role_access where admin_id=?", adminId)
	if e != nil {
		panic(e)
	}
	return untils.ArrayColumn(rows, "role_id")
}

func (a Auth) GetChildrenRoleAdminIds(adminId interface{}, withself ...bool) []interface{} {
	roleIds := a.GetChildrenRoleIds(adminId, withself...)
	return a.GetChildrenAdminIdsByRoleIds(roleIds, adminId, withself...)
}

func (a Auth) GetChildrenAdminIdsByRoleIds(roleIds []interface{}, adminId interface{}, withself ...bool) []interface{} {
	with := false
	if len(withself) > 0 {
		with = withself[0]
	}

	if len(roleIds) == 0 {
		if with {
			return []interface{}{
				adminId,
			}
		}
		return []interface{}{}
	}

	adminRoleAccess, e := a.Db.Query("select admin_id from admin_role_access where role_id in (?) group by admin_id", roleIds)

	if e != nil {
		panic(e)
	}

	adminIds := untils.ArrayColumn(adminRoleAccess, "admin_id")

	if with {
		adminIds = append(adminIds, adminId)
	} else {
		adminIds = untils.ArrayFilter(adminIds, func(val interface{}) bool {
			return fmt.Sprintf("%v", val) != fmt.Sprintf("%v", adminId)
		})
	}

	return adminIds
}

func (a Auth) GetChildrenRoleIdsByRoleId(roleId interface{}, withself ...bool) []interface{} {
	with := false
	if len(withself) > 0 {
		with = withself[0]
	}
	return untils.NewTree().Init(a.getRoles()).GetChildrenIds(roleId, with)
}

func (a Auth) getRoles() []map[string]interface{} {
	rows, e := a.Db.Query("select id,pid,name from admin_role")

	if e != nil {
		panic(e)
	}

	return rows
}

func (a Auth) Log(adminOperationLog map[string]interface{}) error {
	_, e := a.Db.Execute("insert into admin_operation_log (admin_id, username, url, title, content, ip, useragent, create_time)"+
		"VALUES (?, ?, ?, ?, ?, ?, ?, ?)", []interface{}{
		adminOperationLog["admin_id"],
		adminOperationLog["username"],
		adminOperationLog["url"],
		adminOperationLog["title"],
		adminOperationLog["content"],
		adminOperationLog["ip"],
		adminOperationLog["useragent"],
		adminOperationLog["create_time"],
	}...)

	if e != nil {
		return e
	}

	return nil
}
